//
//  KSScrollViewDelegateProxy.m
//  PYMKDynamicUI
//
//  Created by lixuelin on 2021/3/26.
//

#import "KSScrollViewDelegateProxy.h"

static BOOL isInterceptedSecondaryDelegateSelector(SEL sel) {
    return (
            // UIScrollViewDelegate
            sel == @selector(scrollViewDidScroll:) ||
            sel == @selector(scrollViewDidEndScrollingAnimation:) ||
            sel == @selector(scrollViewDidEndDragging:willDecelerate:) ||
            sel == @selector(scrollViewDidEndDecelerating:) ||
            // UICollectionViewDelegate
            sel == @selector(collectionView:willDisplayCell:forItemAtIndexPath:) ||
            // UITableViewDelegate
            sel == @selector(tableView:willDisplayCell:forRowAtIndexPath:)
            );
}

@interface KSScrollViewDelegateProxy ()

@property (strong, nonatomic) NSLock *objLock;

@property (strong, nonatomic) NSPointerArray *ks_SecondaryDelegates;

@end

@implementation KSScrollViewDelegateProxy

- (NSMethodSignature *)methodSignatureForSelector:(SEL)selector {
    NSObject *delegateForResonse = nil;
    if ([self.ks_PrimaryDelegate respondsToSelector:selector]) {
        delegateForResonse = self.ks_PrimaryDelegate;
    } else {
        [self __secondaryDelegatesCompact];
        for (id secondaryDelegate in self.ks_SecondaryDelegates) {
            if ([secondaryDelegate respondsToSelector:selector]) {
                delegateForResonse = secondaryDelegate;
                break;
            }
        }
    }
    return [delegateForResonse respondsToSelector:selector] ? [delegateForResonse methodSignatureForSelector:selector] : nil;
}

- (void)forwardInvocation:(NSInvocation *)invocation {
    if (isInterceptedSecondaryDelegateSelector(invocation.selector)) {
        [self __secondaryDelegatesCompact];
        for (id secondaryDelegate in self.ks_SecondaryDelegates) {
            [self invokeInvocation:invocation onDelegate:secondaryDelegate];
        }
    }
    [self invokeInvocation:invocation onDelegate:self.ks_PrimaryDelegate];
}

- (BOOL)respondsToSelector:(SEL)aSelector {
    return [self.ks_PrimaryDelegate respondsToSelector:aSelector] || [self __secondaryDelegateRespondsToSelector:aSelector];
}

- (BOOL)__secondaryDelegateRespondsToSelector:(SEL)aSelector {
    [self __secondaryDelegatesCompact];
    for (id secondaryDelegate in self.ks_SecondaryDelegates) {
        if ([secondaryDelegate respondsToSelector:aSelector]) {
            return YES;
        }
    }
    return NO;
}

- (void)invokeInvocation:(NSInvocation *)invocation onDelegate:(id<UIScrollViewDelegate>)delegate {
    if ([delegate respondsToSelector:invocation.selector]) {
        [invocation invokeWithTarget:delegate];
    }
}

- (BOOL)containDelegate:(id)delegate {
    BOOL isExist = NO;
    [self.objLock lock];
    [self __secondaryDelegatesCompact];
    for (id secondaryDelegate in self.ks_SecondaryDelegates) {
        if (delegate == secondaryDelegate) {
            isExist = YES;
            break;
        }
    }
    [self.objLock unlock];
    return isExist;
}

- (void)addDelegate:(id)delegate {
    if (delegate == nil) {
        return;
    }
    if ([self containDelegate:delegate]) {
        return;
    }
    [self.objLock lock];
    [self.ks_SecondaryDelegates addPointer:(__bridge void*)delegate];
    [self.objLock unlock];
}

- (NSPointerArray *)ks_SecondaryDelegates {
    if (_ks_SecondaryDelegates == nil) {
        _ks_SecondaryDelegates = [NSPointerArray weakObjectsPointerArray];
    }
    return _ks_SecondaryDelegates;
}

- (NSLock *)objLock {
    if (_objLock == nil) {
        _objLock = [[NSLock alloc] init];
    }
    return _objLock;
}

- (void)__secondaryDelegatesCompact {
    [self.ks_SecondaryDelegates addPointer:NULL];
    [self.ks_SecondaryDelegates compact];
}

@end
